#pragma once

// A ref-counted structure used to hold all the interesting resources
// around the USB device. Ref-counting is required because in-progress
// async operations need to keep this structure around until they
// complete. 
//
// Shutdown is a multi-stage process (see Shutdown() and Close()). 
//  1. The client calls Device->Disconnect()
//  2. The device calls Shutdown on its current DeviceInfo_t
//  3. Shutdown calls Close(false) which only closes the pipes associated
//     with the device
//  4. The client releases its reference to the DeviceInfo_t
//  5. Now the pipes are closed, in-flight operations will complete
//  6. Each of these operations retains a reference to the DeviceInfo_t
//     and keeps this reference until the overlapped result has been
//     retrieved.
//  7. Once the result has been retrieved, they release their reference.
//  8. When all references are released, the destructor fires and the
//     remaining resources are cleaned-up.

class DeviceInfo_t
{
public:
    DeviceInfo_t();
    ~DeviceInfo_t();

    // All the interesting stuff
    WINUSB_INTERFACE_HANDLE winUSBHandle;
    HANDLE deviceHandle;
    BYTE deviceSpeed;
    BYTE bulkInPipe;
    BYTE bulkOutPipe;
    BYTE interruptPipe;

    // Ref counting
    BOOL Initialize(__in GUID* pInterfaceId);
    VOID AddRef()   { InterlockedIncrement(&refCount); }
    VOID Release()  { if (!InterlockedDecrement(&refCount)) delete this; }

    void Shutdown() { Close(false); }

private:
    static const ULONG MAX_DEVPATH_LENGTH   = 256;
    LONG refCount;

    DeviceInfo_t(const DeviceInfo_t&) {}
    void Close(bool fullClose);

    HANDLE OpenDevice(__in GUID* pInterfaceId);
    BOOL   GetDevicePath(__in GUID* pDeviceInterface,
                         __out_ecount(cchDevicePath) PWSTR wszDevicePath,
                         size_t cchDevicePath);
};
